﻿namespace Resharper.Abstractions.CodeDom.Expressions
{
  using System;
  using JetBrains.Annotations;
  using JetBrains.ReSharper.Psi.Tree;
  using Resharper.Abstractions.Actions;
  using Resharper.Abstractions.CodeDom.Expressions.LiteralExpressions;
  using Resharper.Abstractions.CodeDom.Statements;

  /// <summary>Defines the <see cref="LiteralExpressionExtensions"/> class.</summary>
  public static class ExpressionExtensions
  {
    #region Public Methods

    /// <summary>Creates the constant.</summary>
    /// <param name="factory">The factory.</param>
    /// <param name="code">The code.</param>
    /// <returns>Returns the constant.</returns>
    [NotNull]
    public static Expression CreateExpression([NotNull] this Factory factory, [NotNull] string code)
    {
      if (factory == null)
      {
        throw new ArgumentNullException("factory");
      }

      if (code == null)
      {
        throw new ArgumentNullException("code");
      }

      return new Expression(factory.InternalFactory.CreateExpression(code));
    }

    /// <summary>Gets the containing statement.</summary>
    /// <param name="expression">The expression.</param>
    /// <returns>Returns the containing statement.</returns>
    [CanBeNull]
    public static Statement GetContainingStatement(this Expression expression)
    {
      var statement = expression.InternalExpression.GetContainingNode<IStatement>();

      return statement != null ? StatementFactory.GetStatement(statement) : null;
    }

    /// <summary>Creates the expression by value.</summary>
    /// <param name="factory">The factory.</param>
    /// <param name="constantValue">The constant value.</param>
    /// <returns>Returns the expression by value.</returns>
    [CanBeNull]
    public static Expression CreateExpressionByConstantValue([NotNull] this Factory factory, [NotNull] ConstantValue constantValue)
    {
      if (factory == null)
      {
        throw new ArgumentNullException("factory");
      }

      if (constantValue == null)
      {
        throw new ArgumentNullException("constantValue");
      }

      var result = factory.InternalFactory.CreateExpressionByConstantValue(constantValue.InternalConstantValue);

      return result != null ? ExpressionFactory.GetExpression(result) : null;
    }

    /// <summary>Creates the expression.</summary>
    /// <param name="context">The context.</param>
    /// <param name="code">The code.</param>
    /// <returns>Returns the expression.</returns>
    [CanBeNull]
    public static Expression CreateExpression([NotNull] this ExecuteContext context, [NotNull] string code)
    {
      if (context == null)
      {
        throw new ArgumentNullException("context");
      }

      if (code == null)
      {
        throw new ArgumentNullException("code");
      }

      var result = context.Factory.CreateExpression(code);
      
      if (result.IsConstantValue)
      {
        var constantValue = result.ConstantValue;
      
        if (constantValue != null)
        {
          result = context.Factory.CreateExpressionByConstantValue(constantValue);
        }
      }

      return result;
    }

    #endregion
  }
}